#!/usr/bin/python
import rpyc
import datetime
import time
import os
import hashlib
import messages
import cPickle
import sys 
import socket
import fcntl
import struct
import random
from threading import Thread


def get_ip_address(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    return socket.inet_ntoa(fcntl.ioctl(
        s.fileno(),
        0x8915,  # SIOCGIFADDR
        struct.pack('256s', ifname[:15])
    )[20:24])

proc = None
masterProcess = "localhost:6060"
localIP = 'localhost'
localProcess = ''


class MessageHandler(Thread): 
    def __init__(self, msg, destination):
        Thread.__init__(self)
        self.msg = msg
        self.destination = destination
    def run(self):
        conn = getRemoteService(self.destination)
        if self.msg.type == messages.REQ_DATA:
            conn.root.look_up(cPickle.dumps(self.msg))


            
            
            
class FreeNetProc(rpyc.Service):
    
    test = "this is a test"
    
    def on_connect(self):
        print 'a node connected'
         # code that runs when a connection is created (to init the serivce, if needed
        pass
		  
    def on_disconnect(self):
		 # code that runs when the connection has already closed (to finalize the service, if needed)
        pass
    
    def exposed_change(self):
        self.test = "kind works"
    def exposed_myprint(self):
        print self.test
    
    def init (self, prt):
        self.test = "Works!!!"
        print "Initializing FreeNetProc"
        self.routingCache = {}
        self.ip = "localhost"
        self.port = prt
        self.id = getFileKey(self.ip + ":" + str(self.port))
       
        
#       temp1 = hashlib.sha1()
#       temp1.update('a')
#       temp2 = temp1.hexdigest()
#       self.routingCache[temp2] = '145.94.186.245:8080'
        print 'Initialized FreeNetProc with id ' + self.id
        
  #      pid = os.fork()
        
  #      if pid:
  #          self.startCommandLine()
     
  #          print str(pid)
  #          os.kill(pid,15)
  #          sys.exit(0)
  #      else:
  #          return
        
        

    def startCommandLine(self):
        while True:
                command = raw_input('freenet~> ')
                if command == 'exit':
                    print 'Quiting'
                    return
                if command == 'join':
                    join();
                elif command.startswith('search '):
                    query = command[7:]
                    print 'Searching for "' + query + '" ...'
                    
                    msg = messages.Message(messages.REQ_DATA)
                    msg.query = query
                    handler = MessageHandler(msg , localProcess)
                    handler.start()




    def getClosestNode(self, fileKeyHex):
        print 'Looking for closest node to ' + fileKeyHex
        fileKey = hextoint(fileKeyHex)

        keys = self.routingCache.keys()
        keys.sort()
        i=0
        varHex = keys[i]
        var = hextoint(varHex)

        if fileKey < var:
            return self.routingCache[varHex]

        if fileKey > hextoint(keys[len(keys)-1]):
            return self.routingCache[keys[len(keys)-1]]

        i +=1

        while i < len(keys) :
            curhex = keys[i]
            cur = hextoint(curhex)
            prevhex = keys[i-1]
            prev = hextoint(prevhex)
            if fileKey <= cur and fileKey >= prev:
                downdiff = fileKey-prev
                updiff = cur - fileKey
                if downdiff < updiff:
                    res = self.routingCache[prevhex]
                else: 
                    res = self.routingCache[curhex]
                return res
            i+=1
                
    def findNode(self, fileKey):
        if fileKey in self.routingCache:
            print 'Next Node is: ' + self.routingCache[fileKey]
        else:
            print fileKey + ' does not exist in routing cache'
    
   

   
    def lookup(self, msg):
        fileKey = getFileKey(msg.query)
        if datastore_contains(fileKey):
            print 'contains'
        else:
            print 'not contains'
        
        
    def search(self, fileKey):
        print 'Searching for: ' + fileKey
        closest = self.findNode(fileKey)
        
        #sendReq(closest, myIP, htl, depth, fileKey)
        
        
    def sendReq(self,ip, htl, depth, fileKey):
        conn = rpyc.connect(ip, 18861);
        
  
    
    def join(self, msg):
        #check whether it is already registered in cache
        if msg.nodeID in self.routingCache:
            print 'Node "' + self.routingCache[msg.nodeID] +  '" already exist in routing cache'  
        elif msg.nodeID == self.id:
            print 'Message returned. do nothing with routing cache.'
        else:
            self.routingCache[msg.nodeID] = msg.sender
            print 'Node "' + self.routingCache[msg.nodeID] +  '" Added to cache'
        msg.htl = msg.htl - 1
        return
    
    def forwardJoin(self, msg):
        print 'routing cache= ',
        print self.routingCache
        local = localIP + ":"+ str(prt)
        key = random.choice(self.routingCache.keys())
        while self.routingCache[key] == local:
            key = random.choice(self.routingCache.keys())
        address = self.routingCache[key]
        print 'forwarding join request to '  + address
        connection = getRemoteService(address)
#        msg.nodeID = self.id
#        msg.sender = localIP + ":" + str(prt)
        connection.root.recieveJoinAnnouncement(cPickle.dumps(msg))
    #def leave():
        # Yet to write



def getFileKey(descriptor):
            temp = hashlib.sha1()
            temp.update(descriptor)
            return temp.hexdigest()

def hextoint(i):
    return int(i, 16)


def getRemoteService(address):
    split = address.split(':')
    ip = split[0]
    port = split[1]
    print "Initiating a connection to " +  ip  + " on port " + port
    port = int(port)
    connection = rpyc.connect(ip, port)
#    print connection.root.recieveJoinAnnouncement()
    return connection

def join():
    #Start by connecting to master process
    msg = messages.Message(messages.JOIN_ANNOUNCE)
    msg.htl = 5
    msg.nodeID = proc.id
    msg.sender = localIP + ":" + str(prt)
    connection = getRemoteService(masterProcess)
    
    connection.root.recieveJoinAnnouncement(cPickle.dumps(msg))
    

if __name__ =="__main__":

        if len(sys.argv) != 2:
            print "Usage: ./myserver.py port_number"
            sys.exit(1)
        prt = sys.argv[1]




        conn = getRemoteService('localhost:1234')
        conn.root.myprint()


        
